/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package uit.tkorg.coauthorgraph.bo;

import edu.uci.ics.jung.graph.UndirectedSparseGraph;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import uit.tkorg.coauthorgraph.dbaccess.AuthorMapper;
import uit.tkorg.coauthorgraph.dbaccess.AuthorPaperMapper;
import uit.tkorg.coauthorgraph.dbaccess.ConferenceMapper;
import uit.tkorg.coauthorgraph.dbaccess.JournalMapper;
import uit.tkorg.coauthorgraph.dto.AuthorDTO;
import uit.tkorg.coauthorgraph.dto.CrawlAuthorDTO;
import uit.tkorg.coauthorgraph.dto.InputConferenceDTO;
import uit.tkorg.coauthorgraph.dto.InputPaperDTO;
import uit.tkorg.coauthorgraph.dto.PCMemberConfGraphDTO;
import uit.tkorg.coauthorgraph.graph.CoAuthorGraphEdge;
import uit.tkorg.coauthorgraph.graph.CoAuthorGraphVertex;
import uit.tkorg.coauthorgraph.utility.CommonTextFileUtility;
import uit.tkorg.coauthorgraph.utility.HashMapUtility;

/**
 * @author Huynh Ngoc Tin
 *
 */
public class CoAuthorGraphBO {
    /**
     * buildCoAuthorGraph
     * @param authorNameList
     * @param depthLevel
     * @return
     * @throws Exception
     */
    public UndirectedSparseGraph buildCoAuthorGraph(ArrayList authorNameList, String depthLevel) throws Exception {
        UndirectedSparseGraph graph = new UndirectedSparseGraph();
        AuthorMapper authorMapper = new AuthorMapper();
        AuthorPaperMapper authorPaperMapper = new AuthorPaperMapper();
        
        try {
            ArrayList notQueryAuthorList = new ArrayList();
            ArrayList alreadyQueryAuthorList = new ArrayList();
            CrawlAuthorDTO queryingAuthorDTO = new CrawlAuthorDTO();
            if (authorNameList != null) {
                for (int i=0; i<authorNameList.size(); i++) {
                    int curAuthorID = authorMapper.getAuthorID((String) authorNameList.get(i));
                    if (curAuthorID != -1) { // found in author in DB
                        queryingAuthorDTO = new CrawlAuthorDTO();
                        queryingAuthorDTO.setAuthorID(curAuthorID);
                        queryingAuthorDTO.setAuthorName((String) authorNameList.get(i));
                        queryingAuthorDTO.setCrawlDepthLevel(1);
                        notQueryAuthorList.add(queryingAuthorDTO);
                    }
                }
            }

            // Begin of Query & Building CoAuthorGraph Progress
            while ((notQueryAuthorList != null) && (notQueryAuthorList.size() > 0)) {
                queryingAuthorDTO = (CrawlAuthorDTO) notQueryAuthorList.get(0);

                // Check if the depth level is reached and stop the progress of Query & Building CoAuthorGraph.
                if (!depthLevel.equalsIgnoreCase("Unlimited")) {
                    int curQueryDepthLevel = queryingAuthorDTO.getCrawlDepthLevel();
                    if (curQueryDepthLevel > Integer.parseInt(depthLevel)){
                        notQueryAuthorList.clear();
                        break;
                    }
                }

                // Check if author existed in the graph or not --> Add new Vertex to CoAuthorGraph
                CoAuthorGraphVertex queryAuthorVertex = null;
                boolean existInGraph = false;
                Collection vertices = graph.getVertices();
                Iterator iter = vertices.iterator();
                while (iter != null && iter.hasNext()) {
                    CoAuthorGraphVertex authorVertex = (CoAuthorGraphVertex) iter.next();
                    int authorVertexID = authorVertex.getAuthorId();
                    if (queryingAuthorDTO.getAuthorID() == authorVertexID) {
                        existInGraph = true;
                        queryAuthorVertex = authorVertex;
                        break;
                    }
                }
                
                if (!existInGraph) { // add to graph if author not exist in the graph
                    System.out.println("*****************************************************************************");
                    System.out.println("*** Init information for Vertex and add to CoAuthorGraph<V>:" + queryingAuthorDTO.getAuthorName());
                    System.out.println("****************************************************************************");
                    queryAuthorVertex = calculatePropertyForAuthorVertex(queryingAuthorDTO.getAuthorID(),
                                                                                        queryingAuthorDTO.getAuthorName());
                    graph.addVertex(queryAuthorVertex);
                }

                System.out.println("***************************************************************");
                System.out.println("*** Querying for AuthorVertex:" + queryingAuthorDTO.getAuthorName());
                System.out.println("*** Current Depth Level:" + queryingAuthorDTO.getCrawlDepthLevel());
                System.out.println("***************************************************************");

                // get the co-cothor list of the Querying author to add to the list 'notQueryAuthorList'
                ArrayList paperIDList = authorPaperMapper.getPaperIDListOfAnAuthor(queryingAuthorDTO.getAuthorID());
                ArrayList coAuthorList = authorPaperMapper.getCoAuthorDTOList(paperIDList, queryingAuthorDTO.getAuthorID());
                if (coAuthorList != null) {
                    for (int i=0; i<coAuthorList.size(); i++){
                        AuthorDTO coAuthorDTO = (AuthorDTO) coAuthorList.get(i);
                        int curCoAuthorID = coAuthorDTO.getAuthorID();
                        String curCoAuthorName = coAuthorDTO.getAuthorName();
                        // Check if this author exist in the 'notQueryAuthorList' or not
                        boolean found = false;
                        for (int j=0; j<notQueryAuthorList.size(); j++) {
                            CrawlAuthorDTO notQueryAuthorDTO = (CrawlAuthorDTO) notQueryAuthorList.get(j);
                            if (curCoAuthorID == notQueryAuthorDTO.getAuthorID()) {
                                found = true;
                                break;
                            }
                        }

                        // Check if this author exist in the 'alreadyQueryAuthorList' or not
                        for (int k=0; k<alreadyQueryAuthorList.size(); k++) {
                            CrawlAuthorDTO alreadyQueryAuthorDTO = (CrawlAuthorDTO) alreadyQueryAuthorList.get(k);
                            if (curCoAuthorID == alreadyQueryAuthorDTO.getAuthorID()) {
                                found = true;
                                break;
                            }
                        }

                        // add a new co-author into the 'notQueryAuthorList' list.
                        if (found == false) {
                            CrawlAuthorDTO newQueryAuthorDTO = new CrawlAuthorDTO();
                            newQueryAuthorDTO.setAuthorID(curCoAuthorID);
                            newQueryAuthorDTO.setAuthorName(curCoAuthorName);
                            newQueryAuthorDTO.setCrawlDepthLevel(queryingAuthorDTO.getCrawlDepthLevel() + 1);
                            notQueryAuthorList.add(newQueryAuthorDTO);
                        }


                        // Check if author existed in the graph or not --> Add new Vertex to CoAuthorGraph
                        existInGraph = false;
                        vertices = graph.getVertices();
                        iter = vertices.iterator();
                        CoAuthorGraphVertex curAuthorVertex = null;
                        while (iter != null && iter.hasNext()) {
                            curAuthorVertex = (CoAuthorGraphVertex) iter.next();
                            int authorVertexID = curAuthorVertex.getAuthorId();
                            if (curCoAuthorID == authorVertexID) {
                                existInGraph = true;
                                break;
                            }
                        }

                        if (!existInGraph) { // add to graph if author not exist in the graph
                            System.out.println("*****************************************************************************");
                            System.out.println("*** Init information for Vertex and add to CoAuthorGraph<V>:" + curCoAuthorName);
                            System.out.println("****************************************************************************");
                            CoAuthorGraphVertex coAuthorVertex = null;
                            coAuthorVertex = calculatePropertyForAuthorVertex(curCoAuthorID, curCoAuthorName);
                            graph.addVertex(coAuthorVertex);
                            CoAuthorGraphEdge newEdge = new CoAuthorGraphEdge();
                            newEdge.setFirstAuthorID(queryingAuthorDTO.getAuthorID());
                            newEdge.setSecondAuthorID(curCoAuthorID);
                            newEdge.setFirstAuthorName(queryingAuthorDTO.getAuthorName());
                            newEdge.setSecondAuthorName(curCoAuthorName);
                            graph.addEdge(newEdge , queryAuthorVertex, coAuthorVertex);
                        }
                        else {
                            // Kiem tra xem co canh chua --> them canh cua 2 dinh
                            if (queryAuthorVertex != null && curAuthorVertex != null) {
                                CoAuthorGraphEdge newEdge = new CoAuthorGraphEdge();
                                newEdge.setFirstAuthorID(queryingAuthorDTO.getAuthorID());
                                newEdge.setSecondAuthorID(curCoAuthorID);
                                newEdge.setFirstAuthorName(queryingAuthorDTO.getAuthorName());
                                newEdge.setSecondAuthorName(curCoAuthorName);
                                graph.addEdge(newEdge , queryAuthorVertex, curAuthorVertex);
                            }
                        }
                    } // emd of for (...) of coAuthorList
                }

                alreadyQueryAuthorList.add(queryingAuthorDTO);
                // remove the first queryAuthorDTO in the 'notQueryAuthorList' list
                notQueryAuthorList.remove(0);
            } // end of while (...)

            // clear all list
            notQueryAuthorList.clear();
            alreadyQueryAuthorList.clear();
        }
        catch (Exception e) {
            throw e;
        }
        finally {
            authorMapper.closeConnection();
            authorPaperMapper.closeConnection();
        }

        return graph;
    }

    /**
     * calculate and initial properties/values of authorVertex of the CoAuthorGraph
     * @param authorName
     * @return
     * @throws Exception
     */
    public CoAuthorGraphVertex calculatePropertyForAuthorVertex(int authorID, String authorName) throws Exception {
        CoAuthorGraphVertex authorVertex = new CoAuthorGraphVertex();
        AuthorPaperMapper authorPaperMapper = new AuthorPaperMapper();
        ConferenceMapper  confMapper = new ConferenceMapper();
        JournalMapper jourMapper = new JournalMapper();

        try {
            if (authorID != -1) {
                ArrayList authorPaperDTOList = authorPaperMapper.getAuthorPaperDTOList(authorID);
                HashMap totalPaperInEachConfHM = authorPaperMapper.getTotalPaperInEachConfOfAnAuthor(authorID);
                HashMap totalPaperInEachJourHM = authorPaperMapper.getTotalPaperInEachJourOfAnAuthor(authorID);

                authorVertex.setPaperList(authorPaperDTOList);
                authorVertex.setAuthorId(authorID);
                authorVertex.setAuthorName(authorName);
                authorVertex.setConfPaperCount(totalPaperInEachConfHM);
                authorVertex.setJourPaperCount(totalPaperInEachJourHM);
            }

        }
        catch (Exception e) {
            throw e;
        }
        finally {
            authorPaperMapper.closeConnection();
            confMapper.closeConnection();
            jourMapper.closeConnection();
        }

        return authorVertex;
    }

    /**
     * getAuthorVertexIDListInGraph
     * @param graph
     * @return
     * @throws Exception
     */
    public ArrayList getAuthorVertexIDListInGraph(UndirectedSparseGraph graph) throws Exception {
        Collection vertices = graph.getVertices();
        Iterator iter = vertices.iterator();
        CoAuthorGraphVertex curAuthorVertex = null;
        ArrayList authorVertexIDList = new ArrayList();
        while (iter != null && iter.hasNext()) {
            curAuthorVertex = (CoAuthorGraphVertex) iter.next();
            int curAuthorVertexID = curAuthorVertex.getAuthorId();
            authorVertexIDList.add(String.valueOf(curAuthorVertexID));
        }

        return authorVertexIDList;
    }

    /**
     * countOverlapOfTwoGraphOnNodeName
     * @param graph1
     * @param graph2
     * @return
     * @throws Exception
     */
    public int countOverlapOfTwoGraphOnNodeName(UndirectedSparseGraph graph1, UndirectedSparseGraph graph2) throws Exception {
        int numberOfOverlapedNode = 0;
        try {
            Collection verticesGraph1 = graph1.getVertices();
            Iterator iterGraph1 = verticesGraph1.iterator();
            CoAuthorGraphVertex curAuthorVertexGraph1 = null;
            while (iterGraph1 != null && iterGraph1.hasNext()) {
                curAuthorVertexGraph1 = (CoAuthorGraphVertex) iterGraph1.next();
                String curAuthorVertexNameGraph1 = curAuthorVertexGraph1.getAuthorName();
                
                Collection verticesGraph2 = graph2.getVertices();
                Iterator iterGraph2 = verticesGraph2.iterator();
                CoAuthorGraphVertex curAuthorVertexGraph2 = null;
                while (iterGraph2 != null && iterGraph2.hasNext()) {
                    curAuthorVertexGraph2 = (CoAuthorGraphVertex) iterGraph2.next();
                    String curAuthorVertexNameGraph2 = curAuthorVertexGraph2.getAuthorName();
                    if (curAuthorVertexNameGraph1.equalsIgnoreCase(curAuthorVertexNameGraph2)) {
                        numberOfOverlapedNode += 1;
                        break;
                    }
                }
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
        finally {
        }

        return numberOfOverlapedNode;
    }

    /**
     * CalculateMetricAndExportToFile
     * @param inputConfList
     * @param fileName
     * @throws Exception
     */
    public void calculateMetricAndExportToFile(ArrayList inputConfList, String out_Conf_Class_FileName, String output_PC_Conf_FileName) throws Exception {
        StringBuffer outStringBuffer1 = new StringBuffer(); // use for output_conf_classification.txt
        StringBuffer outStringBuffer2 = new StringBuffer(); // use for output_PC_Conf.txt

        ConferenceBO confBO = new ConferenceBO();
        AuthorPaperMapper authorPaperMapper = new AuthorPaperMapper();
        AuthorMapper authorMapper = new AuthorMapper();
        DecimalFormat df = new DecimalFormat("#.##");
        try {
            ArrayList initConfNameList = new ArrayList();
            for (int i=0; i<inputConfList.size(); i++) {
                InputConferenceDTO inConfDTO = (InputConferenceDTO) inputConfList.get(i);
                initConfNameList.add(inConfDTO.getConfName());
            }

            // build co-author network for pc-members all conferences
            ArrayList pcMemberGraphList = new ArrayList();
            for (int i=0; i<inputConfList.size(); i++) {
                PCMemberConfGraphDTO pcMemberConfGraphDTO = new PCMemberConfGraphDTO();
                InputConferenceDTO inConfDTO = (InputConferenceDTO) inputConfList.get(i);
                ArrayList pcMemberNameList = inConfDTO.getPcMemberList();
                // hard code for level = 2 to build network for PC Members
                UndirectedSparseGraph pcMemberCoAuthorGraph = buildCoAuthorGraph(pcMemberNameList, "1");
                pcMemberConfGraphDTO.setConfName(inConfDTO.getConfName());
                pcMemberConfGraphDTO.setGraph(pcMemberCoAuthorGraph);

                pcMemberGraphList.add(pcMemberConfGraphDTO);
            }

            System.out.println("-------------------------------------------------");
            System.out.println("Calculate metrics 1, 2, 3, 4 for the conferences");
            System.out.println("-------------------------------------------------");
            // Loop through all conf in the list
            for (int i=0; i<inputConfList.size(); i++) {
                InputConferenceDTO inConfDTO = (InputConferenceDTO) inputConfList.get(i);
                String confName = inConfDTO.getConfName();
                outStringBuffer1.append("###" + confName + "\n");
                outStringBuffer1.append("###" + confName + "\n");
                System.out.println(i+ "." + confName);

                System.out.println("-----------------------------------------------------------");
                System.out.println("Calculate metrics 1, 2, 3, 4 for papers");
                System.out.println("-----------------------------------------------------------");
                ArrayList inputPaperDTOList = inConfDTO.getPaperList();
                for (int j=0; j<inputPaperDTOList.size(); j++) {
                    InputPaperDTO inPaperDTO = (InputPaperDTO) inputPaperDTOList.get(j);
                    int depthLevel = inPaperDTO.getDepthLevelForCoAuthorNet();
                    ArrayList authorNameList = inPaperDTO.getAuthorNameList();
                    ArrayList initialAuthorIDList = authorMapper.getAuthorIDListFromNameList(authorNameList);

                    // build co-author network for authors in the current paper
                    System.out.println(j+ "." + inPaperDTO.getTitle());
                    System.out.println("Building co-author network for all authors of the paper");
                    UndirectedSparseGraph coAuthorGraph = buildCoAuthorGraph(authorNameList, String.valueOf(depthLevel));
                    ArrayList authorVertexIDList = getAuthorVertexIDListInGraph(coAuthorGraph);
                    System.out.println("Finish building graph");

                    outStringBuffer1.append("#" + inPaperDTO.getTitle() + "\n");
                    outStringBuffer1.append("Rank\t" + "ConfName" + "\t\t\t\t\t\t\t\t\t\t" + "Metric#1\t" +
                            "Top1\t" + "Top2\t" + "Top3\t" + "Top4\t" + "Top5\t" + "\n");
                    outStringBuffer2.append("#" + inPaperDTO.getTitle() + "\n");
                    outStringBuffer2.append("Rank\t" + "ConfName-PCmembers" + "\t\t\t\t\t\t\t\t\t" + "#Distance\t" +
                            "Top1\t" + "Top2\t" + "Top3\t" + "Top4\t" + "Top5\t" + "\n");

                    // Calculate METRIC #1 for inPaperDTO
                    HashMap metric1_Conf_HashMap = authorPaperMapper.getTotalPaperInEachConfOfAuthorList(authorVertexIDList, initConfNameList);
                    metric1_Conf_HashMap = HashMapUtility.getSortedMapDescending(metric1_Conf_HashMap);
                    // Add top 5 conf after calculate METRIC #1
                    if (metric1_Conf_HashMap != null && metric1_Conf_HashMap.size() > 0) {
                        Set metric1Set = metric1_Conf_HashMap.entrySet();
                        Iterator confIter = metric1Set.iterator();
                        int order = 1;
                        while(confIter.hasNext()){
                            Map.Entry metric1_MapEntry = (Map.Entry)confIter.next();
                            outStringBuffer1.append(String.valueOf(order) + "\t" + (String)metric1_MapEntry.getKey() + "\t\t\t\t"
                                    + metric1_MapEntry.getValue() + "\n");
                            order++;
                            if (order == 6) break; // get top 5 conference in 16
                        }
                    }

                    outStringBuffer1.append("Rank\t" + "ConfName" + "\t\t\t\t\t\t\t\t\t\t" + "Metric#2\t" +
                            "Top1\t" + "Top2\t" + "Top3\t" + "Top4\t" + "Top5\t" + "\n");

                    // Calculate METRIC #2 for inPaperDTO
                    HashMap metric2_Conf_HashMap = confBO.calculateTotalConferencePercentage(authorVertexIDList, initConfNameList);
                    metric2_Conf_HashMap = HashMapUtility.getSortedMapDescending(metric2_Conf_HashMap);

                    // Add top 5 conf after calculate METRIC #2
                    if (metric2_Conf_HashMap != null && metric2_Conf_HashMap.size() > 0) {
                        Set metric2_Set = metric2_Conf_HashMap.entrySet();
                        Iterator confIter = metric2_Set.iterator();
                        int order = 1;
                        while(confIter.hasNext()){
                            Map.Entry metric2_MapEntry = (Map.Entry)confIter.next();
                            outStringBuffer1.append(String.valueOf(order) + "\t" + (String)metric2_MapEntry.getKey() + "\t\t\t\t"
                                    + df.format(metric2_MapEntry.getValue())  + "\n");
                            order++;
                            if (order == 6) break; // get top 5 conference in 16
                        }
                    }

                    outStringBuffer1.append("Rank\t" + "ConfName" + "\t\t\t\t\t\t\t\t\t\t" + "Metric#3\t" +
                            "Top1\t" + "Top2\t" + "Top3\t" + "Top4\t" + "Top5\t" + "\n");

                    // Calculate METRIC #3 for inPaperDTO
                    HashMap metric3_Conf_HashMap = confBO.calculateTotalConfPercentWithCoAuthor(authorVertexIDList, initialAuthorIDList, initConfNameList);
                    metric3_Conf_HashMap = HashMapUtility.getSortedMapDescending(metric3_Conf_HashMap);

                    // Add top 5 conf after calculate METRIC #3
                    if (metric3_Conf_HashMap != null && metric3_Conf_HashMap.size() > 0) {
                        Set metric3_Set = metric3_Conf_HashMap.entrySet();
                        Iterator confIter = metric3_Set.iterator();
                        int order = 1;
                        while(confIter.hasNext()){
                            Map.Entry metric3_MapEntry = (Map.Entry)confIter.next();
                            outStringBuffer1.append(String.valueOf(order)  + "\t" + (String)metric3_MapEntry.getKey() + "\t\t\t\t"
                                    + df.format(metric3_MapEntry.getValue()) + "\n");
                            order++;
                            if (order == 6) break; // get top 5 conference in 16
                        }
                    }

                    // Calculate METRIC #4 for inPaperDTO - PCMembers of conference
                    for (int k=0; k<pcMemberGraphList.size(); k++) {
                        PCMemberConfGraphDTO pcMemberGraphDTO = (PCMemberConfGraphDTO) pcMemberGraphList.get(k);
                        String conferenceName = pcMemberGraphDTO.getConfName();
                        UndirectedSparseGraph pcMemberGraph = pcMemberGraphDTO.getGraph();
                        int networkDistance = countOverlapOfTwoGraphOnNodeName(coAuthorGraph, pcMemberGraph);
                        outStringBuffer2.append(String.valueOf(k+1) + "\t" + conferenceName + "\t\t\t\t\t" + networkDistance + "\n");
                    }

                }

                outStringBuffer1.append("\n\n");
                outStringBuffer2.append("\n\n");
            }

            // Write to Text File
            CommonTextFileUtility.writeTextFile(out_Conf_Class_FileName, outStringBuffer1.toString()); // metric 1, 2, 3
            CommonTextFileUtility.writeTextFile(output_PC_Conf_FileName, outStringBuffer2.toString()); // metric 4

        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        finally {
            authorPaperMapper.closeConnection();
            authorMapper.closeConnection();
        }
    }
}
